package cn.com.cetccst.activation.network;

import static cn.com.cetccst.tools.device.Device.getDeviceId;
import static cn.com.cetccst.tools.device.Device.getOsv;
import static cn.com.cetccst.tools.kv.CommonKV.kvStore;
import static cn.com.cetccst.tools.network.Wifi.getMacAddress;
import static cn.com.cetccst.tools.phone.Phone.getPhone1Number;
import static cn.com.cetccst.tools.phone.Phone.getProviderName;
import static cn.com.cetccst.tools.pm.AppPackage.getSignatureHash;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;
import android.util.Base64;

import androidx.annotation.NonNull;

import com.google.gson.Gson;

import java.io.IOException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import cn.com.cetccst.activation.R;
import cn.com.cetccst.activation.kv.KVKeys;
import cn.com.cetccst.activation.network.response.AuthCodeResponse;
import cn.com.cetccst.activation.network.response.AuthResponse;
import cn.com.cetccst.activation.network.response.AuthTypeResponse;
import cn.com.cetccst.activation.network.response.FeaturesResponse;
import cn.com.cetccst.activation.network.response.MAMSSOResponse;
import cn.com.cetccst.activation.network.response.MDMSSOResponse;
import cn.com.cetccst.activation.network.response.OTAResponse;
import cn.com.cetccst.activation.ui.viewmodel.ActivationViewModel;
import cn.com.cetccst.network.IRetrofitClient;
import cn.com.cetccst.network.RetrofitManager;
import cn.com.cetccst.network.log.IHttpLog;
import cn.com.cetccst.tools.device.Device;
import cn.com.cetccst.tools.encrypt.Encryption;
import cn.com.cetccst.tools.log.CommonLogger;
import cn.com.cetccst.tools.pm.AppPackage;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import retrofit2.Retrofit;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

public class ActivationHttpRequester {

    private static final String MEDIA_JSON_TYPE = "application/json;charset=UTF-8";
    private static final String X_VERSION = "X-version";
    private static final String AUTHENTICATION = "Authentication";
    public static final String PROTO_VERSION = "5.4";
    public static final String API_VERSION_NAME = "version";
    public static final String OS_VERSION = "X-OSVersion";
    public static final String DEVICE_TYPE = "X-DType";
    public static final String BUILD_TYPE = "X-BuildType";

    private static IRetrofitClient retrofitClient;
    private static IHttpLog httpLogConfig;
    private static IActivationApiService apiService;
    private static ActivationHttpRequester httpRequester;

    private ActivationHttpRequester() {
    }

    public static void initialize(Context context, String baseUrl) {
        if (retrofitClient == null) {
            retrofitClient = new RetrofitManager.Builder(context, baseUrl)
                    .setLogLevel(IHttpLog.Level.BODY)
                    .addInterceptor(new Interceptor() {
                        @NonNull
                        @Override
                        public Response intercept(@NonNull Chain chain) throws IOException {
                            Request originalRequest = chain.request();
                            Request.Builder authorisedRequest = originalRequest.newBuilder();
                            addHeaderIfNecessary(originalRequest, authorisedRequest, X_VERSION, PROTO_VERSION);
                            if (originalRequest.url().pathSegments().contains("caas"))
                                addHeaderIfNecessary(originalRequest, authorisedRequest, AUTHENTICATION, kvStore != null ? kvStore.decodeString(KVKeys.KEY_ACCESS_TOKEN) : null);
                            addHeaderIfNecessary(originalRequest, authorisedRequest, API_VERSION_NAME, PROTO_VERSION);
                            addHeaderIfNecessary(originalRequest, authorisedRequest, DEVICE_TYPE, Build.BRAND + "_" + Build.MODEL);
                            addHeaderIfNecessary(originalRequest, authorisedRequest, OS_VERSION, getOsv());
                            addHeaderIfNecessary(originalRequest, authorisedRequest, BUILD_TYPE, "du");
                            return chain.proceed(authorisedRequest.build());
                        }
                    })
                    .build();
            Retrofit retrofit = retrofitClient.getRetrofit();
            httpLogConfig = retrofitClient.getHttpLogConfig();
            apiService = retrofit.create(IActivationApiService.class);
        }
    }

    private static void addHeaderIfNecessary(Request orgRequest, Request.Builder newRequest, String headerName, String headerContent) {
        if (TextUtils.isEmpty(orgRequest.header(headerName)) && !TextUtils.isEmpty(headerContent)) {
            newRequest.header(headerName, headerContent);
        }
    }

    public static IHttpLog getHttpLogConfig() {
        return httpLogConfig;
    }

    public static ActivationHttpRequester getInstance() {
        if (httpRequester == null) {
            httpRequester = new ActivationHttpRequester();
        }
        return httpRequester;
    }

    public Observable<OTAResponse> requestOTA() {
        return apiService.ota().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<AuthTypeResponse> requestActivation(Context context) {
        Map<String, Object> agentBody = new HashMap<>();
        agentBody.put("regId", context.getString(R.string.sso_reg_id));
        agentBody.put("appSign", getSignatureHash(context, context.getPackageName(), AppPackage.SignatureAlgorithm.ALGORITHM_SHA256));
        agentBody.put("appPkg", context.getPackageName());

        Map<String, Object> body = new HashMap<>();
        body.put("platform", "ANDROID");
        body.put("deviceSerialNumber", getDeviceId(context));
        body.put("engine", agentBody);
        RequestBody requestBody = requestBody(body);
        return apiService.activationCode(requestBody).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<AuthResponse> requestAuth(ActivationViewModel.AuthData authData, String activationCode) {
        Map<String, Object> data = new HashMap<>();
        data.put("email", authData.getUserId());
        data.put("authCode", authData.getAccessKey());
        Map<String, Object> body = new HashMap<>();
        body.put("authType", authData.getAuthType());
        body.put("authData", data);
        RequestBody requestBody = requestBody(body);
        return apiService.auth(activationCode, requestBody).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<FeaturesResponse> requestFeatures() {
        return apiService.features().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<AuthCodeResponse> requestAuthCode(Context context, String packageName, String regId) {
        Map<String, Object> body = new HashMap<>();
        body.put("regId", regId);
//        body.put("appSign", context.getPackageName().equals(packageName) ?
//                SignatureUtils.getCurrentSignatureHash(context, SignatureUtils.ALGORITHM_SHA256) :
//                SignatureUtils.getPackageSignatureHash(context, packageName, SignatureUtils.ALGORITHM_SHA256));
        body.put("appSign", getSignatureHash(context, context.getPackageName(), AppPackage.SignatureAlgorithm.ALGORITHM_SHA256));
        body.put("appPkg", packageName);
        body.put("includRemoteSDK", false);
        RequestBody requestBody = requestBody(body);
        return apiService.authcode(requestBody).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<MDMSSOResponse> requestMDMSSO(Context context, String userId, String oAuthCode) {

        KeyPair pair = null;
        try {
            pair = Encryption.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

        Map<String, Object> request = new HashMap<>();
        request.put("oauthCode", oAuthCode);
        request.put("email", userId);
        request.put("enrollmentKey", oAuthCode);
        request.put("imei", getDeviceId(context));
        request.put("solution", 1);
        request.put("deviceSignedCert", "mock_cert");
        request.put("deviceRoutingClass", 0);
        request.put("serviceRoutingId", 0);
        request.put("serviceRoutingIdAtBoot", 0);

        PublicKey pubKey = pair.getPublic();
        String devicePubKey = new String(Encryption.base64.encode(pubKey.getEncoded(), Base64.NO_WRAP));
        request.put("devicePubKey", devicePubKey);
        request.put("protocolVersion", 4);

        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            request.put("emmVersion", packageInfo.versionName);
        } catch (PackageManager.NameNotFoundException e) {
            CommonLogger.error("Enroll request buildPayload get emm version failed.", e);
            request.put("emmVersion", "1.0");
        }
        Map<String, Object> deviceProperties = new HashMap<>();
        deviceProperties.put("device_product", Build.MANUFACTURER);
        deviceProperties.put("device_model", Build.MODEL);
        deviceProperties.put("device_devicename", Device.getSerialNumber());
        deviceProperties.put("device_lang", Locale.getDefault().getDisplayLanguage());
        deviceProperties.put("device_mac", getMacAddress(context));
        deviceProperties.put("device_phone_number", getPhone1Number(context));
        deviceProperties.put("device_carrier", getProviderName(context));
        deviceProperties.put("device_sdk_version", Build.VERSION.SDK_INT);
        deviceProperties.put("device_SwV", Build.VERSION.RELEASE);
        // must use the ID provided by SDK
        try {
            deviceProperties.put("device_uuid", getDeviceId(context));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        request.put("deviceProperties", deviceProperties);
        RequestBody requestBody = requestBody(request);
        return apiService.mdmSSO(requestBody).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    public Observable<MAMSSOResponse> requestMAMSSO(Context context, String oAuthCode) {
        return apiService.mamSSO("Bearer " + oAuthCode, context.getPackageName(), getDeviceId(context)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    private RequestBody requestBody(Object body) {
        MediaType mediaType = MediaType.parse(MEDIA_JSON_TYPE);
        String bodyStr = new Gson().toJson(body);
        return RequestBody.create(bodyStr, mediaType);
    }
}
